using System;
using System.Collections.Generic;
using NHibernate;
using NHibernate.Criterion;

namespace YN.Core
{
    public abstract class AbstractDao<T>
    {

        #region Properties

        protected ISession Session
        {
            get
            {
                return SessionManager.Instance.GetSession();
            }
        }

        #endregion

        #region Public Methods

        public void BeginTransaction()
        {
            SessionManager.Instance.BeginTransaction();
        }

        public void CommitChanges()
        {
            if (SessionManager.Instance.HasOpenTransaction())
            {
                SessionManager.Instance.CommitTransaction();
            }
            else
            {
                SessionManager.Instance.GetSession().Flush();
            }
        }

        public void CommitTransaction()
        {
            if (SessionManager.Instance.HasOpenTransaction())
            {
                SessionManager.Instance.CommitTransaction();
            }
        }

        public void Delete(T entity)
        {
            if (entity != null)
                Session.Delete(entity);
        }

        public T Get(int id)
        {
            return Session.Get<T>(id);
        }

        public IList<T> GetAllObjects()
        {
            return GetByCriteria();
        }

        public int GetCount()
        {
            return Session.CreateCriteria(typeof(T))
                .SetProjection(Projections.RowCount())
                .UniqueResult<int>();
        }

        public IList<T> Order(string propertyName, bool ascending)
        {
            return Session.CreateCriteria(typeof(T))
                .AddOrder(new Order(propertyName, ascending))
                .List<T>();
        }

        public IList<T> Order(string propertyName, bool ascending, int limit)
        {
            return Session.CreateCriteria(typeof(T))
                .AddOrder(new Order(propertyName, ascending))
                .SetMaxResults(limit)
                .List<T>();
        }

        public IList<T> Order(string propertyName, bool ascending, int start, int limit)
        {
            if (start == 0)
            {
                return Order(propertyName, ascending, limit);
            }
            else
            {
                return Session.CreateCriteria(typeof(T))
                    .AddOrder(new Order(propertyName, ascending))
                    .SetFirstResult(start)
                    .SetMaxResults(limit)
                    .List<T>();
            }
        }

        public T Save(T entity)
        {
            if (entity != null)
                Session.Save(entity);

            return entity;
        }

        public T SaveOrUpdate(T entity)
        {
            if (entity != null)
                Session.SaveOrUpdate(entity);

            return entity;
        }

        public T Update(T entity)
        {
            if (entity != null)
                Session.Update(entity);

            return entity;
        }

        #endregion

        #region Protected Methods

        protected IList<T> GetByCriteria(params ICriterion[] criterion)
        {
            ICriteria criteria = Session.CreateCriteria(typeof(T));

            foreach (ICriterion criterium in criterion)
            {
                criteria.Add(criterium);
            }

            return criteria.List<T>();
        }

        protected IList<T> GetByExample(T exampleInstance, params string[] propertiesToExclude)
        {
            Example example = Example.Create(exampleInstance);

            foreach (string propertyToExclude in propertiesToExclude)
            {
                example.ExcludeProperty(propertyToExclude);
            }

            return Session.CreateCriteria(typeof(T)).Add(example).List<T>();
        }

        protected int GetCount(IQuery query)
        {
            return Convert.ToInt32(query.UniqueResult<long>());
        }

        protected int GetCount(string hql)
        {
            return Convert.ToInt32(Session.CreateQuery(hql).UniqueResult<long>());
        }

        protected int GetCountByNamed(string queryName)
        {
            return Convert.ToInt32(Session.GetNamedQuery(queryName).UniqueResult<long>());
        }

        protected IList<T> NamedQuery(string queryName)
        {
            return Session.GetNamedQuery(queryName).List<T>();
        }

        protected IList<T> NamedQuery(string queryName, int limit)
        {
            return Session.GetNamedQuery(queryName)
                .SetMaxResults(limit)
                .List<T>();
        }

        protected IList<T> NamedQuery(string queryName, int start, int limit)
        {
            if (start == 0)
            {
                return NamedQuery(queryName, limit);
            }
            else
            {
                return Session.GetNamedQuery(queryName)
                    .SetFirstResult(start)
                    .SetMaxResults(limit)
                    .List<T>();
            }
        }

        protected IList<T> Query(string hql)
        {
            return Session.CreateQuery(hql).List<T>();
        }

        protected IList<T> Query(string hql, int limit)
        {
            return Session.CreateQuery(hql)
                .SetMaxResults(limit)
                .List<T>();
        }

        protected IList<T> Query(string hql, int start, int limit)
        {
            if (start == 0)
            {
                return Query(hql, limit);
            }
            else
            {
                return Session.CreateQuery(hql)
                    .SetFirstResult(start)
                    .SetMaxResults(limit)
                    .List<T>();
            }
        }

        protected IList<T> Query(IQuery query)
        {
            return query.List<T>();
        }

        protected IList<T> Query(IQuery query, int limit)
        {
            return query.SetMaxResults(limit).List<T>();
        }

        protected IList<T> Query(IQuery query, int start, int limit)
        {
            if (start == 0)
            {
                return query.SetMaxResults(limit).List<T>();
            }
            else
            {
                return query.SetFirstResult(start).SetMaxResults(limit).List<T>();
            }
        }

        #endregion

    }
}
